home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Scene Storm
/
Scene Storm - Volume 1.iso
/
coding
/
c
/
cc
/
cc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-11-14
|
20KB
|
985 lines
/**
*** cc.c Unix compatible frontend for Amiga's C compilers
***
*** This program is free software; you can redistribute it and/or modify
*** it under the terms of the GNU General Public License as published by
*** the Free Software Foundation; either version 2 of the License, or
*** (at your option) any later version.
***
*** This program is distributed in the hope that it will be useful,
*** but WITHOUT ANY WARRANTY; without even the implied warranty of
*** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*** GNU General Public License for more details.
***
*** You should have received a copy of the GNU General Public License
*** along with this program; if not, write to the Free Software
*** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***
***
*** Compiler: dcc V3.01
***
*** Computer: Amiga 1200
***
*** Author: Jochen Wiedmann
*** Am Eisteich 9
*** 72555 Metzingen
*** Germany
***
*** Phone: (49)7123 / 14881
*** Internet: wiedmann@zdv.uni-tuebingen.de
***
***
***
*** This is a Unix compatible frontend for gcc, SAS/C and Dice. In fact,
*** it does nothing than else than calling the appropriate frontends
*** with the appropriate options.
***
*** Supported options are:
***
*** -v Verbose (try it :-)
*** -c Don't link
*** -a Compile only, don't assemble
*** -E Run preprocessor only
*** -I<dir> Look for include files in directory <dir>
*** -L<dir> Look for libraries in directory <dir>
*** -o<file> Set the name of the created file; it is recommended
*** to use this as the respective frontends might behave
*** different in selecting default names.
*** -D<symbol> Defines preprocessor symbol; use -Dsymbol=var for
*** specific values.
*** -U<symbol> Undefine the preprocessor symbol <symbol>.
*** -l<lib> Link with library <lib>.
*** -g Turn debugging on.
*** -O Optimize
***
**/
/**
*** Version string
**/
#define VERSION 1
#define REVISION 1
#define DATE "11.10.94"
#define VERS "cc 1.1"
#define VSTRING "cc 1.1 (11.10.94)"
#define VERSTAG "\0$VER: cc 1.1 (11.10.94)"
char Version[] = VSTRING;
char AmigaVersion[] = VERSTAG;
/**
*** Include files
**/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <exec/lists.h>
#include <exec/nodes.h>
#include <clib/alib_protos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <pragmas/exec_pragmas.h>
#include <pragmas/dos_pragmas.h>
extern struct Library *SysBase;
extern struct Library *DOSBase;
/**
*** List of options processed by cc:
**/
typedef enum
{
OPTION_UNKNOWN,
OPTION_VERBOSE,
OPTION_DEFINE,
OPTION_UNDEFINE,
OPTION_PREPROCESSOR_ONLY,
OPTION_ASSEMBLER_ONLY,
OPTION_NOLINK,
OPTION_INCLUDEDIR,
OPTION_LINKDIR,
OPTION_OPTIMIZE,
OPTION_DEBUGGING,
OPTION_LIBRARY,
OPTION_OUTPUT
} CompilerOption;
/**
*** This structure defines an element in the list of options.
**/
typedef struct
{
struct MinNode mn;
CompilerOption Option;
char *Arg;
} CurrentOption;
/**
*** Compatibility mode (Default: gcc)
**/
enum
{
COMPATIBILITYMODE_GCC,
COMPATIBILITYMODE_SAS,
COMPATIBILITYMODE_DICE,
} CompatibilityMode = COMPATIBILITYMODE_SAS;
char *CompatibilityModeNames[] =
{
"-gcc",
"-sas",
"-dice"
};
/**
*** Compiler mode (Default: Everything)
**/
enum
{
COMPILERMODE_PREPROCESSOR_ONLY,
COMPILERMODE_COMPILER_ONLY,
COMPILERMODE_ASSEMBLER_ONLY,
COMPILERMODE_EVERYTHING,
} CompilerMode = COMPILERMODE_EVERYTHING;
char *CompilerModeNames[] =
{
"-E",
"-a",
"-c"
};
/**
*** These lists are used to hold the current options.
**/
struct List OptionList;
/**
*** Guess, what this function does? :-)
**/
void Usage(void)
{
printf("%s%s%s", "Usage: cc [options] [files]\n\
\n\
Options known to cc are:\n\
\n\
-v\t\tbe verbose\n\
-o<file>\t\tsend output to <file> (usage is recommended)\n\
-E\t\trun preprocessor only\n\
-a\t\trun preprocessor and compiler only\n\
-c\t\trun preprocessor, compiler and assembler, don't link\n\
-I<dir>\t\tlook for include files in <dir>\n\
-L<dir>\t\took for link libraries in <dir>\n\
-l<lib>\t\tlink with library <lib>\n\
-g\t\tturn debugging on\n\
-O\t\tturn optimization on\n\
-D<sym>\t\tdefine preprocessor symbol\n\
-U<sym>\t\tundefine preprocessor symbol\n\
-h,--help,?\tprint this message\n", Version, " UNIX 'cc' Front-End for SAS/C Compiler\n\n");
exit(5);
}
/**
*** This functions adds a new option to the list of options.
**/
void AddOption(CompilerOption co, char *arg)
{
CurrentOption *cu;
if (!(cu = malloc(sizeof(*cu))))
{
fprintf(stderr, "Out of memory!");
exit(20);
}
cu->Option = co;
cu->Arg = arg;
AddTail(&OptionList, (struct Node *) cu);
}
/**
*** This function handles options which may be splitted into two
*** arguments or not, like -I <dir> or -I<dir>.
**/
void AddOptionArg(CompilerOption co,
int *i,
int argc,
char *argv[])
{
char *arg = &argv[*i][2];
if (!*arg)
{
if (++(*i) >= argc)
{
fprintf(stderr, "%s: Missing argument: %s\n", argv[--(*i)]);
exit(20);
}
arg = argv[*i];
}
AddOption(co, arg);
}
/**
*** This function is used to set the compatibility mode.
**/
void SetCompatibilityMode(int mode, int *set)
{
if (*set)
{
fprintf(stderr, "cc, warning: %s overwrites %s\n",
CompatibilityModeNames[mode],
CompatibilityModeNames[CompatibilityMode]);
exit(10);
}
*set = TRUE;
CompatibilityMode = mode;
}
/**
*** This function is used to set the compatibility mode.
**/
void SetCompilerMode(int mode, int *set)
{
if (*set)
{
fprintf(stderr, "cc warning: %s overwrites %s\n",
CompilerModeNames[mode],
CompilerModeNames[CompilerMode]);
exit(10);
}
*set = TRUE;
CompilerMode = mode;
}
/**
*** This function is used to build the string that is used to call
*** the real frontend. It supports strings of any length, thus it
*** may look somewhat complicated.
**/
void AddToString(char **strptr, char *format, ...)
{
static char buffer[1024]; /* Maximal length of *one* argument */
static int RealLen;
static int MaxLen;
int len;
va_list args;
va_start(args, format);
vsprintf(buffer, format, args);
len = strlen(buffer);
if (!*strptr)
{
MaxLen = 0;
}
if (len + RealLen + 1 > MaxLen)
{
/**
*** Current buffer not sufficient, allocate a new buffer
**/
char *newstr;
if (!(newstr = malloc(MaxLen+1024)))
{
fprintf(stderr, "Out of memory!");
exit(20);
}
MaxLen += 1024;
if (*strptr)
{
strcpy(newstr, *strptr);
}
else
{
*newstr = '\0';
RealLen = 0;
}
*strptr = newstr;
}
strcpy(*strptr + RealLen, buffer);
RealLen += len;
}
/**
*** This function is used to parse the arguments. Options will
*** be included into OptionList, files in FileList.
**/
void ParseArgs(int argc,
char *argv[])
{
int i;
/**
*** These variables are used for checking if arguments repeat.
*** Note that we keep them local: This allows, for example,
*** to use different settings in the environment and on the
*** command line.
**/
int CompatibilityModeIsSet = FALSE;
int CompilerModeIsSet = FALSE;
for (i = 0; i < argc; i++)
{
char *argvi = argv[i];
if (argvi[0] == '-')
{
/**
*** Assume this to be a compiler option.
**/
switch(argvi[1])
{
case '-':
if (strcmp(argvi, "--help") == 0)
{
Usage();
}
AddOption(OPTION_UNKNOWN, argvi);
break;
case 'D':
AddOptionArg(OPTION_DEFINE, &i, argc, argv);
break;
case 'E':
switch(argvi[2])
{
case '\0':
SetCompilerMode(COMPILERMODE_PREPROCESSOR_ONLY,
&CompilerModeIsSet);
break;
default:
AddOption(OPTION_UNKNOWN, argvi);
break;
}
break;
case 'I':
AddOptionArg(OPTION_INCLUDEDIR, &i, argc, argv);
break;
case 'L':
AddOptionArg(OPTION_LINKDIR, &i, argc, argv);
break;
case 'O':
AddOption(OPTION_OPTIMIZE, &argvi[2]);
break;
case 'U':
AddOptionArg(OPTION_UNDEFINE, &i, argc, argv);
break;
case 'a':
switch(argvi[2])
{
case '\0':
SetCompilerMode(COMPILERMODE_COMPILER_ONLY,
&CompilerModeIsSet);
break;
default:
AddOption(OPTION_UNKNOWN, argvi);
break;
}
break;
case 'c':
switch(argvi[2])
{
case '\0':
SetCompilerMode(COMPILERMODE_ASSEMBLER_ONLY,
&CompilerModeIsSet);
break;
default:
AddOption(OPTION_UNKNOWN, argvi);
break;
}
break;
case 'd':
if (strcmp(argvi,
CompatibilityModeNames[COMPATIBILITYMODE_DICE]) == 0)
{
SetCompatibilityMode(COMPATIBILITYMODE_DICE,
&CompatibilityModeIsSet);
}
else
{
AddOption(OPTION_UNKNOWN, argvi);
}
break;
case 'g':
switch(argvi[2])
{
case '\0':
AddOption(OPTION_DEBUGGING, argvi);
break;
default:
if (strcmp(argvi,
CompatibilityModeNames[COMPATIBILITYMODE_GCC]) == 0)
{
SetCompatibilityMode(COMPATIBILITYMODE_GCC,
&CompatibilityModeIsSet);
}
else
{
AddOption(OPTION_UNKNOWN, argvi);
}
break;
}
break;
case 'h':
switch(argvi[2])
{
case '\0':
Usage();
default:
AddOption(OPTION_UNKNOWN, argvi);
break;
}
break;
case 'l':
AddOptionArg(OPTION_LIBRARY, &i, argc, argv);
break;
case 'o':
AddOptionArg(OPTION_OUTPUT, &i, argc, argv);
break;
case 's':
if (strcmp(argvi,
CompatibilityModeNames[COMPATIBILITYMODE_SAS]) == 0)
{
SetCompatibilityMode(COMPATIBILITYMODE_SAS,
&CompatibilityModeIsSet);
}
else
{
AddOption(OPTION_UNKNOWN, argvi);
}
break;
case 'v':
switch(argvi[2])
{
case '\0':
AddOption(OPTION_VERBOSE, argvi);
break;
default:
AddOption(OPTION_UNKNOWN, argvi);
break;
}
break;
default:
AddOption(OPTION_UNKNOWN, argvi);
break;
}
}
else if (strcmp(argvi, "?") == 0)
{
Usage();
}
else
{
AddOption(OPTION_UNKNOWN, argvi);
}
}
}
/**
*** This function calls gcc as a frontend.
**/
int CompileGcc(void)
{
CurrentOption *co;
char *CompileString = NULL;
int Verbose = FALSE;
AddToString(&CompileString, "gcc");
for (co = (CurrentOption *) OptionList.lh_Head;
co->mn.mln_Succ != NULL;
co = (CurrentOption *) co->mn.mln_Succ)
{
switch (co->Option)
{
case OPTION_UNKNOWN:
AddToString(&CompileString, " \"%s\"", co->Arg);
break;
case OPTION_VERBOSE:
AddToString(&CompileString, " -v");
Verbose = TRUE;
break;
case OPTION_DEFINE:
AddToString(&CompileString, " \"-D%s\"", co->Arg);
break;
case OPTION_UNDEFINE:
AddToString(&CompileString, " \"-U%s\"", co->Arg);
break;
case OPTION_PREPROCESSOR_ONLY:
AddToString(&CompileString, " -E");
break;
case OPTION_ASSEMBLER_ONLY:
AddToString(&CompileString, " -a");
break;
case OPTION_NOLINK:
AddToString(&CompileString, " -c");
break;
case OPTION_INCLUDEDIR:
AddToString(&CompileString, " \"-I%s\"", co->Arg);
break;
case OPTION_LINKDIR:
AddToString(&CompileString, " \"-L%s\"", co->Arg);
break;
case OPTION_OPTIMIZE:
AddToString(&CompileString, " \"-O%s\"", co->Arg);
break;
case OPTION_DEBUGGING:
AddToString(&CompileString, " -g");
break;
case OPTION_LIBRARY:
AddToString(&CompileString, " \"-l%s\"", co->Arg);
break;
case OPTION_OUTPUT:
AddToString(&CompileString, " -o \"%s\"", co->Arg);
break;
}
}
AddToString(&CompileString, "\n");
if (Verbose)
{
printf("%s\n%s", Version, CompileString);
}
return(system(CompileString));
}
/**
*** This function calls SAS/C as a frontend.
**/
int CompileSAS(void)
{
CurrentOption *co;
char *CompileString = NULL;
int Verbose = FALSE;
char *OptionOutput = NULL;
AddToString(&CompileString, "sc");
for (co = (CurrentOption *) OptionList.lh_Head;
co->mn.mln_Succ != NULL;
co = (CurrentOption *) co->mn.mln_Succ)
{
switch (co->Option)
{
case OPTION_UNKNOWN:
AddToString(&CompileString, " \"%s\"", co->Arg);
break;
case OPTION_VERBOSE:
AddToString(&CompileString, " VERBOSE");
Verbose = TRUE;
break;
case OPTION_DEFINE:
AddToString(&CompileString, " DEF \"%s\"", co->Arg);
break;
case OPTION_UNDEFINE: /* Not supported */
break;
case OPTION_INCLUDEDIR:
AddToString(&CompileString, " IDIR\"%s\"", co->Arg);
break;
case OPTION_LINKDIR: /* Not supported */
break;
case OPTION_OPTIMIZE:
AddToString(&CompileString, " OPTIMIZE");
break;
case OPTION_DEBUGGING:
AddToString(&CompileString, " DEBUG FULLFLUSH");
break;
case OPTION_LIBRARY:
AddToString(&CompileString, " LIBRARY \"%s.lib\"", co->Arg);
break;
case OPTION_OUTPUT:
OptionOutput = co->Arg;
break;
}
}
switch(CompilerMode)
{
case COMPILERMODE_PREPROCESSOR_ONLY:
AddToString(&CompileString, " PPONLY");
case COMPILERMODE_ASSEMBLER_ONLY:
if (OptionOutput)
{
AddToString(&CompileString, " OBJNAME \"%s\"", OptionOutput);
}
break;
case COMPILERMODE_COMPILER_ONLY:
if (!OptionOutput)
{
OptionOutput = "*";
}
AddToString(&CompileString, " \"DISASM=%s\"", OptionOutput);
break;
case COMPILERMODE_EVERYTHING:
if (OptionOutput)
{
AddToString(&CompileString, " PNAME \"%s\"", OptionOutput);
}
AddToString(&CompileString, " LINK");
break;
}
AddToString(&CompileString, "\n");
if (Verbose)
{
printf("%s\n%s", Version, CompileString);
}
return(system(CompileString));
}
/**
*** This function calls Dice as a frontend.
**/
int CompileDice(void)
{
CurrentOption *co;
char *CompileString = NULL;
int Verbose = FALSE;
switch(CompilerMode)
{
case COMPILERMODE_PREPROCESSOR_ONLY:
AddToString(&CompileString, "dcpp");
break;
case COMPILERMODE_COMPILER_ONLY:
AddToString(&CompileString, "dcc -a");
break;
case COMPILERMODE_ASSEMBLER_ONLY:
AddToString(&CompileString, "dcc -c");
break;
default:
AddToString(&CompileString, "dcc");
break;
}
for (co = (CurrentOption *) OptionList.lh_Head;
co->mn.mln_Succ != NULL;
co = (CurrentOption *) co->mn.mln_Succ)
{
switch (co->Option)
{
case OPTION_UNKNOWN:
AddToString(&CompileString, " \"%s\"", co->Arg);
break;
case OPTION_VERBOSE:
if (CompilerMode != COMPILERMODE_PREPROCESSOR_ONLY)
{
AddToString(&CompileString, " -v", co->Arg);
}
Verbose = TRUE;
break;
case OPTION_DEFINE:
AddToString(&CompileString, " \"-D%s\"", co->Arg);
break;
case OPTION_UNDEFINE: /* Not supported */
break;
case OPTION_INCLUDEDIR:
AddToString(&CompileString, " \"-I%s\"", co->Arg);
break;
case OPTION_LINKDIR:
if (CompilerMode != COMPILERMODE_PREPROCESSOR_ONLY)
{
AddToString(&CompileString, " \"-L%s\"", co->Arg);
}
break;
case OPTION_OPTIMIZE: /* Not suported */
break;
case OPTION_DEBUGGING:
if (CompilerMode != COMPILERMODE_PREPROCESSOR_ONLY)
{
AddToString(&CompileString, " -s -d1");
}
break;
case OPTION_LIBRARY:
if (CompilerMode != COMPILERMODE_PREPROCESSOR_ONLY)
{
AddToString(&CompileString, " \"-l%s\"", co->Arg);
}
break;
case OPTION_OUTPUT:
AddToString(&CompileString, " -o \"%s\"", co->Arg);
break;
}
}
AddToString(&CompileString, "\n");
if (Verbose)
{
printf("%s\n%s", Version, CompileString);
}
return(system(CompileString));
}
/**
*** This function is used to split a string into arguments.
*** It returns an array similar to argv.
***
*** I don't like doing things for myself, but as far as I
*** can see neither SAS nor Dice offer a possibility of
*** doing this.
***
*** Inputs: argstr - the string to split into arguments,
*** for example an AmigaDOS command line;
*** may contain arguments like
*** "This is one argument"
*** or
*** "Note the "" inside this argument"
*** where the double quotation mark will be
*** changed into one.
*** argc - pointer to an int where to store the
*** number of arguments found in argstr
***
*** Result: an NULL terminated array of pointers to the
*** arguments or NULL; note that the pointers go
*** into argstr and argstr will be modified.
**/
char **SplitArgs(char *argstr, int *argcptr)
{
int argc;
char *argptr;
char **argv;
char **argvptr;
/**
*** Parse the string for the first time counting
*** the number of arguments.
**/
argptr = argstr;
argc = 0;
for(;;)
{
/**
*** Skip blanks
**/
while (*argptr == ' ' || *argptr == '\t')
{
++argptr;
}
if (*argptr == '\0' || *argptr == '\n' || *argptr == '\r')
{
break;
}
++argc;
if (*argptr == '\"')
{
do
{
++argptr;
if (*argptr == '\"')
{
if (*(argptr+1) == '\"')
{
++argptr;
}
else
{
break;
}
}
}
while (*argptr != '\0' && *argptr != '\r' && *argptr != '\n');
}
else
{
while (*argptr != '\0' && *argptr != '\r' && *argptr != '\n' &&
*argptr != '\t' && *argptr != ' ')
{
++argptr;
}
}
{
char c;
c = *argptr;
*(argptr++) = '\0';
if (c == '\0' || c == '\r' || c == '\n')
{
break;
}
}
}
*argcptr = argc;
if (!(argv = malloc(sizeof(char *) * (argc+1))))
{
return(NULL);
}
/**
*** Parse the string a second time
**/
for (argvptr = argv, argptr = argstr; argc > 0; --argc, ++argvptr)
{
int inside;
while (*argptr == ' ' || *argptr == '\t')
{
++argptr;
}
if (*argptr == '\"')
{
++argptr;
inside = TRUE;
}
else
{
inside = FALSE;
}
*argvptr = argptr;
while (*argptr)
{
if (*argptr == '\"' && inside)
{
char *ptr;
char *oldptr;
/**
*** Found a "", remove the second ".
**/
for(ptr = argptr++, oldptr = *argvptr;
ptr >= oldptr; --ptr)
{
*(ptr+1) = *ptr;
}
*argvptr = oldptr+1;
}
++argptr;
}
++argptr;
}
*argvptr = NULL;
return(argv);
}
/**
*** Finally main().
**/
int main(int argc, char *argv[])
{
char *cflags;
NewList(&OptionList);
if ((cflags = getenv("CCOPT")))
{
int envargc;
char **envargv;
if (!(envargv = SplitArgs(cflags, &envargc)))
{
fprintf(stderr, "Out of memory!\n");
exit(20);
}
ParseArgs(envargc, envargv);
}
ParseArgs(argc-1, argv+1);
switch(CompatibilityMode)
{
case COMPATIBILITYMODE_GCC:
CompileGcc();
break;
case COMPATIBILITYMODE_SAS:
CompileSAS();
break;
case COMPATIBILITYMODE_DICE:
CompileDice();
break;
}
}